package org.smile.excel.poi;

import org.apache.poi.ss.usermodel.*;
import org.smile.beans.BeanUtils;
import org.smile.beans.converter.ConvertException;
import org.smile.beans.converter.type.DoubleConverter;
import org.smile.beans.converter.type.IntegerConverter;
import org.smile.beans.converter.type.LongConverter;
import org.smile.collection.CollectionUtils;
import org.smile.commons.SmileRunException;
import org.smile.commons.Strings;
import org.smile.excel.function.IFunction;
import org.smile.function.BaseFunctionInfo;
import org.smile.json.JSON;
import org.smile.math.NumberUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

/**
 * excel表格操作
 *
 * @author 胡真山
 */
public abstract class HeaderExcel extends BaseExcel {
    /**
     * 默认的表头行高
     */
    protected static final int DEFAULT_HEADER_ROW_H = 18;

    /**
     * 默认的数据行高
     */
    protected static final int DEFAULT_DATA_ROW_H = 15;
    /**
     * 表头信息
     */
    protected ExcelHeader[] headers;
    /**
     * 标题单元格样式
     */
    protected CellStyle headerCellStyle;
    /**
     * 数据单元格样式
     */
    protected CellStyle dataCellStyle;

    protected ExcelExportCellStyles cellStyles;
    /**用于缓存单位格样式*/
    protected Map<String, CellStyle> cellStyleCache = new HashMap<>();


    public HeaderExcel(ExcelHeader... headers) {
        try {
            this.workbook = getNewInstanceClass().newInstance();
            this.headers = headers;
            initCellStyle();
            for (ExcelHeader header : headers) {
                Sheet sheet = workbook.createSheet(header.getSheetName());
                createTitleRows(sheet, header);
                setColumnWidth(sheet, header);
            }
        } catch (Exception e) {
            throw new SmileRunException(e);
        }
    }

    /**
     * 初始化数据单元格样式和表头样式
     */
    protected void initCellStyle() {
        if (this.cellStyles == null) {
            cellStyles = new ExcelExportCellStyles();
        }
        this.dataCellStyle = createDefaultDataStyle(workbook);
        this.headerCellStyle = createDefaultHeadStyle(workbook);
    }

    /**
     * 初始化工作表  可能是2007 2003格式
     *
     * @return
     */
    protected abstract Class<? extends Workbook> getNewInstanceClass();

    /**
     * 设置表格列的宽度
     *
     * @param sheet
     * @param header
     */
    protected void setColumnWidth(Sheet sheet, ExcelHeader header) {
        Integer[] columnWidths = header.getColumnWidth();
        if (columnWidths != null) {
            for (int i = 0; i < columnWidths.length; i++) {
                sheet.setColumnWidth(i, DEFAULT_FONT_WIDTH * columnWidths[i]);
            }
        }
    }

    /**
     * 创建一个sheet的表头
     *
     * @param sheet
     * @param header
     */
    protected void createTitleRows(Sheet sheet, ExcelHeader header) {
        int startRow = header.getStartRow();
        int startColumn = header.getStartColumn();
        for (String[] title : header.getHeaderTexts()) {
            createTitleRows(sheet, title, startRow++, startColumn);
        }
        int cols = header.getHeaderCols().length;
        mergeColumns(sheet, startColumn, startColumn + cols);
    }

    /**
     * 以一个数组方式创建一个标题行
     *
     * @param title
     */
    protected void createTitleRows(Sheet sheet, String[] title, int rowIndex, int startColumn) {
        int length = title.length;
        Row row = sheet.createRow(rowIndex);
        int mergeStartColumn = startColumn;
        String mergeStartColumnName = title[0];
        for (int i = startColumn; i < length; i++) {
            Cell cell = row.createCell(i);
            cell.setCellValue(title[i]);
            cell.setCellStyle(headerCellStyle);
            if (!mergeStartColumnName.equals(title[i])) {
                if (i - mergeStartColumn > 1) {
                    merge(sheet, rowIndex, rowIndex, mergeStartColumn, i - 1);
                }
                mergeStartColumn = i;
            } else if (i == length - 1) {
                if (i - mergeStartColumn > 0) {
                    merge(sheet, rowIndex, rowIndex, mergeStartColumn, i);
                }
            }
            mergeStartColumnName = title[i];
        }
    }

    /**
     * 默认表头单元格样式
     *
     * @param workbook
     * @return
     */
    protected CellStyle createDefaultHeadStyle(Workbook workbook) {
       return  this.cellStyles.defaultHeadStyle(workbook);
    }

    /**
     * 默认表头单元格样式
     *
     * @param workbook
     * @return
     */
    protected CellStyle createDefaultDataStyle(Workbook workbook) {
        return this.cellStyles.defaultDataStyle(workbook);
    }

    /**
     * 从一个列表中创建出一个工作空间
     *
     * @param list
     */
    public void fillDataList(int sheetIndex, Collection<?> list) {
        try {
            /**创建表中的数据信息*/
            ExcelHeader header = headers[sheetIndex];
            int rowNum = header.getDataStartRow();
            Sheet sheet = this.workbook.getSheetAt(sheetIndex);
            dataRowNumber = 0;
            for (Object rowData : list) {
                dataRowNumber++;
                Row row = createDataRow(sheet, rowNum++);
                if (header.isPropertyFill()) {
                    fillPropertyRowData(header, row, rowData);
                } else {
                    fillRowData(header, row, rowData);
                }
            }
            merge(sheet, header, list);
        } catch (Exception e) {
            throw new SmileRunException("创建excel出错", e);
        }
    }

    /**
     * @param list
     */
    public void fillDataList(Collection<?> list) {
        fillDataList(0, list);
    }

    /**
     * 根据要合并的单元格信息对所有单元格进行合并
     *
     * @param sheet
     */
    protected void merge(Sheet sheet, ExcelHeader header, Collection<?> dataSource) {
        if (CollectionUtils.notEmpty(header.getMergeSets())) {
            List<MergeSetLoop<Object>> loops = new LinkedList<MergeSetLoop<Object>>();
            //初始化合并循环
            List<MergeSet> mergeSets = header.getMergeSets();
            int startRowIndex = header.getDataStartRow();
            for (MergeSet config : mergeSets) {
                loops.add(new MergeSetLoop<Object>(config, startRowIndex));
            }
            merge(sheet, dataSource, loops, startRowIndex);
        }
    }

    /**
     * 创建数据行
     *
     * @param sheet
     * @param rowNum
     * @return
     */
    protected Row createDataRow(Sheet sheet, int rowNum) {
        Row row = sheet.createRow(rowNum++);
        row.setHeightInPoints(DEFAULT_DATA_ROW_H);
        return row;
    }

    /**
     * 创建表头行
     *
     * @param sheet
     * @param rowNum
     * @return
     */
    protected Row createHeaderRow(Sheet sheet, int rowNum) {
        Row row = sheet.createRow(rowNum++);
        row.setHeightInPoints(DEFAULT_HEADER_ROW_H);
        return row;
    }

    /**
     * 创建一个数据单元格
     *
     * @param row
     * @param cellIndex
     * @return
     */
    protected Cell createDataCell(Row row, int cellIndex) {
        Cell cell = row.createCell(cellIndex);
        cell.setCellStyle(dataCellStyle);
        return cell;
    }

    /**
     * 不使用属性填充数据行
     * 即使是 map的时候也按顺序进行填充
     *
     * @param header
     * @param row
     * @param rowData
     */
    protected void fillRowData(ExcelHeader header, Row row, Object rowData) {
        int colNum = header.getStartColumn();
        if (rowData instanceof Object[]) {
            Object[] temp = (Object[]) rowData;
            for (Object obj : temp) {
                createDataCell(row, colNum++).setCellValue(convertToString(obj));
            }
        } else if (rowData instanceof Map) {
            Map map = (Map) rowData;
            for (Object obj : map.values()) {
                createDataCell(row, colNum++).setCellValue(convertToString(obj));
            }
        } else {
            try {
                Map map = BeanUtils.mapFromBean(rowData);
                for (Object obj : map.values()) {
                    createDataCell(row, colNum++).setCellValue(convertToString(obj));
                }
            } catch (Exception e) {
                logger.error("填充excel行数据出错 row:" + row.getRowNum() + " 数据：" + JSON.toJSONString(rowData), e);
            }
        }
    }

    /**
     * 写入已知类型的数据
     *
     * @param cell
     * @param column
     * @param value
     * @throws ConvertException
     */
    private void writeDataTypeClassValue(ExcelHeader header,Cell cell, String column, Object value) throws ConvertException {
        ColumnProperty property=header.getColumnProperty(column);
        if(property==null){
            cell.setCellValue(convertToString(value));
        }else{
            Class dataTypeClass=property.getDataTypeClass();
            Map options = property.getOptions();
            if(options!=null){
                Object val = options.get(value);
                if(val==null){
                    cell.setCellValue(convertToString(value));
                }else{
                    cell.setCellValue(convertToString(val));
                }
            }else if (Number.class.isAssignableFrom(dataTypeClass)) {
                cell.setCellType(CellType.NUMERIC);
                if (value == null) {
                    cell.setCellValue(0);
                } else {
                    if (Long.class == dataTypeClass) {
                        Long l = LongConverter.instance.convert(value);
                        cell.setCellValue(NumberUtils.format(l, "#"));
                    } else if (Integer.class == dataTypeClass || Short.class == dataTypeClass) {
                        Integer l = IntegerConverter.instance.convert(value);
                        cell.setCellValue(l);
                    } else {
                        Double d = DoubleConverter.instance.convert(value);
                        createFormatterStyle(cell, defaultNumberFormat());
                        cell.setCellValue(d);
                    }
                }
            } else if (Date.class.isAssignableFrom(dataTypeClass)) {
                createFormatterStyle(cell, dateFormatPattern(property));
                if (value instanceof Date) {
                    cell.setCellValue((Date) value);
                } else {
                    setCellValueData(cell, value);
                }
            } else {
                cell.setCellValue(convertToString(value));
            }
        }
    }

    private String defaultNumberFormat(){
        return "#0.####";
    }
    /**
     * 又精度的单元格样式
     *
     * @param cell
     */
    protected void createFormatterStyle(Cell cell, String  formatter) {
        if (formatter != null) {
            cell.setCellStyle(buildCellStyle(formatter));
        }
    }

    /**
     * 导出日期格式
     * @return
     */
    protected String dateFormatPattern(ColumnProperty property) {
        String formatter = property.getFormatPattern();
        if (formatter != null) {
                return formatter;
        }
        return "yyyy-MM-dd";
    }

    protected CellStyle buildCellStyle(String pattern) {
        CellStyle cellStyle = this.cellStyleCache.get(pattern);
        if (cellStyle == null) {
            cellStyle = this.workbook.createCellStyle();
            cellStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat(pattern));
            this.cellStyleCache.put(pattern, cellStyle);
        }
        return cellStyle;
    }


    /**
     * 使用属性填充数据行
     *
     * @param header
     * @param row
     * @param rowData
     */
    protected void fillPropertyRowData(ExcelHeader header, Row row, Object rowData) {
        int colNum = header.getStartColumn();
        String[] cols = header.getHeaderCols();
        if (rowData instanceof Object[]) {
            Object[] temp = (Object[]) rowData;
            for (String col : cols) {
                int index = Integer.valueOf(col);
                setCellValueData(createDataCell(row, colNum++), temp[index]);
            }
        } else {
            for (String col : cols) {
                Object value = getCellExpValue(rowData, col);
                setCellValueData(createDataCell(row, colNum++), value);
            }
        }
    }

    protected void setCellValueData(Cell cell, Object value) {
        setCellValue(cell, value);
    }

    /**
     * 从单元格的表达式计算出单元格的值
     * 一个单元格的值
     *
     * @param oneData
     * @param exp     单元格中的表达式 可以是属性  变量  定义的常量
     * @return
     */
    private Object getCellExpValue(Object oneData, String exp) {
        if (exp.startsWith(VARIABLE)) {
            //template属性变量表达式
            try {
                return invokeGetValue(this, exp.substring(2));
            } catch (Exception e) {
                logger.error("获取变量" + exp + "出错", e);
                return Strings.BLANK;
            }
        } else {
            BaseFunctionInfo fun = functionHandler.getFunctionInfo(exp);
            if (functionHandler.isFunction(fun)) {
                //函数表达式
                IFunction function = functionHandler.getFunction(fun);
                Object value = null;
                String argExp = fun.getArgExpression()[0];
                //函数是不是需要当前的表达式取值用于转换
                if (function.needFieldValue()) {
                    value = getPropertyExpValue(oneData, argExp);
                }
                return function.convert(oneData, argExp, value);
            }
            return getPropertyExpValue(oneData, exp);
        }

    }

    public void write(OutputStream os) throws IOException {
        this.workbook.write(os);
    }

    /**
     * 注册函数
     * @param name
     * @param function
     */
    public void registFunction(String name, IFunction function) {
        this.functionHandler.registerFunction(name, function);
    }
}
